package com.example.futureai.service.impl;

import cn.hutool.core.util.StrUtil;
import com.example.futureai.service.TextSplitterIntf;
import org.springframework.ai.document.Document;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * @ClassName: RecursiveCharacterTextSplitter
 * @Description:
 * @Author: YourName
 * @Date: 2025/2/24
 */

public class RecursiveCharacterTextSplitter implements TextSplitterIntf {
    /**
     * 分隔符列表，用于拆分文本
     * 中文："\n\n", "。", "！", "？", "，", "；"
     * 英文："\n\n", "\n", " "
     */
    private List<String> separators = Arrays.asList("\n\n", "\n", " ");
    /**
     * 每个块的最大大小
     */
    private int chunkSize = 250;
    /**
     * 块之间的重叠大小
     */
    private int chunkOverlap = 30;

    public RecursiveCharacterTextSplitter() {
    }

    public RecursiveCharacterTextSplitter(int chunkSize, int chunkOverlap) {
        this.chunkSize = chunkSize;
        this.chunkOverlap = chunkOverlap;

        if (chunkOverlap >= chunkSize) {
            throw new IllegalArgumentException("chunkOverlap must be smaller than chunkSize");
        }
    }

    public RecursiveCharacterTextSplitter(List<String> separators, int chunkSize, int chunkOverlap) {
        this.separators = separators;
        this.chunkSize = chunkSize;
        this.chunkOverlap = chunkOverlap;

        if (chunkOverlap >= chunkSize) {
            throw new IllegalArgumentException("chunkOverlap must be smaller than chunkSize");
        }
    }

    /**
     * Spring ai Document 拆分
     *
     * @param documents
     * @param metaData  文档元数据信息
     * @return
     */
    @Override
    public List<Document> split(List<Document> documents, List<Map<String, Object>> metaData) {
        List<Document> chunkedDocuments = new ArrayList<>();
        documents.forEach(document -> {
            List<String> chunks = splitText(document.getText());
            chunks.forEach(chunk -> {
                chunkedDocuments.add(new Document(chunk, metaData.get(0)));
            });
        });
        return chunkedDocuments;
    }

    /**
     * 分割文本
     *
     * @param text 待分割的文本
     * @return 分割后的文本块列表
     */
    public List<String> splitText(String text) {
        List<String> finalChunks = new ArrayList<>();
        String separator = "";
        for (String s : separators) {
            if (s.isEmpty() || text.contains(s)) {
                separator = s;
                break;
            }
        }

        List<String> splits = separator.isEmpty() ? Arrays.asList(text.split("")) : Arrays.asList(text.split(separator, -1));
        List<String> goodSplits = new ArrayList<>();
        for (String s : splits) {
            if (s.length() < chunkSize) {
                goodSplits.add(s);
            }
            else {
                if (!goodSplits.isEmpty()) {
                    List<String> mergedText = mergeSplits(goodSplits, separator);
                    finalChunks.addAll(mergedText);
                    goodSplits.clear();
                }
                List<String> otherInfo = splitText(s);
                finalChunks.addAll(otherInfo);
            }
        }
        if (!goodSplits.isEmpty()) {
            List<String> mergedText = mergeSplits(goodSplits, separator);
            finalChunks.addAll(mergedText);
        }
        return finalChunks;
    }

    /**
     * 将分割后的文本片段合并成符合指定大小的块
     *
     * @param splits
     * @param separator
     * @return
     */
    private List<String> mergeSplits(List<String> splits, String separator) {
        int separatorLen = separator.length();
        List<String> docList = new ArrayList<>();
        List<String> curPartList = new ArrayList<>();
        int totalLen = 0;

        for (String split : splits) {
            int curLen = split.length();
            if (totalLen + curLen + (curPartList.size() > 0 ? separatorLen : 0) > chunkSize) {
                if (totalLen > chunkSize) {
                    // 打印警告信息
                    System.out.println("Warn:Created a chunk of size " + totalLen + " which is longer than the specified " + chunkSize);
                }
                if (!curPartList.isEmpty()) {
                    String doc = joinTextParts(curPartList, separator);
                    if (!StrUtil.isEmpty(doc)) {
                        docList.add(doc);
                    }
                    while ((totalLen > chunkOverlap) || ((totalLen + curLen + (curPartList.size() > 0 ? separatorLen : 0) > chunkSize) && totalLen > 0)) {
                        totalLen -= curPartList.get(0).length() + (curPartList.size() > 1 ? separatorLen : 0);
                        curPartList = curPartList.subList(1, curPartList.size());

                    }
                }
            }
            curPartList.add(split);
            totalLen += curLen + (curPartList.size() > 1 ? separatorLen : 0);

        }
        String doc = joinTextParts(curPartList, separator);
        if (!StrUtil.isEmpty(doc)) {
            docList.add(doc);
        }
        return docList;
    }

    private String joinTextParts(List<String> docPartsList, String separator) {
        String res = StrUtil.join(separator,docPartsList);
        res = res.trim();
        return res;
    }
}
